package com.young.logpool;

import com.young.entity.Log;
import com.young.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public class BatchInsertLogThreadManager {
    //日志批量插入的数量，10条日志
    private int BATCH_SIZE=5;

    //日志插入执行的最大时间间隔，单位毫秒
    private long MAX_EXE_TIME=10000;

    //日志队列的最大容量
    private int MAX_QUEUE_SIZE=100;

    //线程睡眠时间，具体时间需要结合项目实际情况，单位毫秒
    private int SLEEP_TIME=500;

    @Autowired
    @Qualifier(value = "batchInsertLogPool")
    private ThreadPoolExecutor batchInsertLogPool;

    @Autowired
    private LogService logService;

    //任务队列，存放日志内容
    private BlockingQueue<Log>queue=new LinkedBlockingDeque<>(MAX_QUEUE_SIZE);

    //原子变量，用来判断是否循环
    private AtomicBoolean run=new AtomicBoolean(true);

    //记录任务队列中的任务数量
    private AtomicInteger logCount=new AtomicInteger(0);

    //上次执行日志插入时的时间
    private long lastExecuteTime;

    @PostConstruct
    public void init(){
        log.info("init--------------------");
        lastExecuteTime=System.currentTimeMillis();
        batchInsertLogPool.execute(new Runnable() {
            @Override
            public void run() {
                while (run.get()){
                    try{
                        //线程休眠，具体时间根据项目的实际情况配置
                        Thread.sleep(SLEEP_TIME);
                    }catch (InterruptedException e){
                    }
                    //满足放入10个日志
                    if (logCount.get()>=BATCH_SIZE||(System.currentTimeMillis()-lastExecuteTime)>MAX_EXE_TIME){
                        log.info("满足要求了===================,queue.size():{},isEmpty:{}",queue.size(),queue.isEmpty());
                        if (logCount.get()>0){
                            List<Log>list=new ArrayList<>();
                            /**
                             *  drainTo (): 一次性从BlockingQueue获取所有可用的数据对象（还可以指定获取数据的个数），
                             *  通过该方法，可以提升获取数据效率；不需要多次分批加锁或释放锁。
                             *  将取出的数据放入指定的list集合中
                             */
                            queue.drainTo(list);
                            //任务队列中任务数量置为0
                            logCount.set(0);
                            //从线程池中取出线程执行日志插入
                            batchInsertLogPool.execute(new BatchInsertLogThread(list,logService));
                        }
                        //获取当前执行的时间
                        lastExecuteTime=System.currentTimeMillis();
                    }
                }
            }
        });
    }

    /**
     * 将日志放入队列中
     */
    public boolean addLog(Log log)throws Exception{
        if (logCount.get()>=MAX_QUEUE_SIZE){
            //当队列满时，直接将日志丢弃
            return false;
        }
        //将日志放入任务队列中
        this.queue.offer(log);
        //队列中的任务数量+1
        logCount.incrementAndGet();
        return true;
    }

    /**
     * 关闭线程池
     */
    public void shutdown(){
        //结束while循环
        run.set(false);
        //关闭线程池
        batchInsertLogPool.shutdown();
        batchInsertLogPool.shutdownNow();
    }
}
